package com.ly.heart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ly.common.PageParam;
import com.ly.common.core.domain.AjaxResult;
import com.ly.common.core.domain.entity.SysDept;
import com.ly.common.core.domain.entity.SysUser;
import com.ly.common.exception.CustomException;
import com.ly.common.utils.DateUtils;
import com.ly.common.utils.SecurityUtils;
import com.ly.common.utils.StringUtils;
import com.ly.common.utils.erp.CommonUtil;
import com.ly.common.utils.erp.ConstUtil;
import com.ly.common.utils.erp.KingConstUtil;
import com.ly.common.utils.erp.SnowflakeIdWorker;
import com.ly.heart.domain.*;
import com.ly.heart.domain.dto.KingFinancePayRefundDto;
import com.ly.heart.domain.dto.StartProcessDto;
import com.ly.common.core.domain.ActCurrentTaskVo;
import com.ly.heart.domain.vo.KingFinancePayRefundVo;
import com.ly.heart.mapper.KingFinancePayRefundMapper;
import com.ly.heart.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ly.heart.service.king.PayRefundPushService;
import com.ly.heart.util.ChineseToPinyin;
import com.ly.system.service.ISysDeptService;
import com.ly.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 付款退款单 服务实现类
 * </p>
 * @author WuZH
 * @since 2024-08-04
 */
@Slf4j
@Service
public class KingFinancePayRefundServiceImpl extends ServiceImpl<KingFinancePayRefundMapper, KingFinancePayRefundEntity> implements IKingFinancePayRefundService {

    @Resource
    private KingFinancePayRefundMapper kingFinancePayRefundMapper;
    @Resource
    private IKingFinancePayDetailService iKingFinancePayDetailService;
    @Resource
    private IKingFinancePayRefundDetailService iKingFinancePayRefundDetailService;
    @Resource
    private IActModelService iActModelService;
    @Resource
    private ICustomerService iCustomerService;
    @Resource
    private ISupplierService iSupplierService;
    @Resource
    private ISysDeptService iSysDeptService;
    @Resource
    private ISysUserService iSysUserService;
    @Resource
    private IKingBaseTransactionsCompanyService iKingBaseTransactionsCompanyService;
    @Resource
    private IKingBaseBankService iKingBaseBankService;
    @Resource
    private IKingPayableReceivableVerificationDetailService iKingPayableReceivableVerificationDetailService;
    @Resource
    private IKingFinancePayService iKingFinancePayService;
    @Resource
    private IActTaskCommentService iActTaskCommentService;
    @Resource
    private IOrderService iOrderService;
    @Resource
    private PayRefundPushService payRefundPushService;


    @Override
    public AjaxResult queryList (KingFinancePayRefundEntity kingFinancePayRefundEntity) {
        kingFinancePayRefundEntity.setIsUsed ( ConstUtil.IS_USED_YES );
        kingFinancePayRefundEntity.setPage ( ( kingFinancePayRefundEntity.getPage () - 1 ) * kingFinancePayRefundEntity.getLimit () );
        List<KingFinancePayRefundEntity> kingFinancePayRefundEntities = kingFinancePayRefundMapper.queryList ( kingFinancePayRefundEntity );
        kingFinancePayRefundEntities.forEach ( payRefundEntity -> {
            String transactionCompanyName = getCompanyName ( payRefundEntity.getTransactionCompanyType (), payRefundEntity.getTransactionCompanyId (), 1 );
            payRefundEntity.setTransactionCompanyName ( transactionCompanyName );
            String payCompanyName = getCompanyName ( payRefundEntity.getPayDeptType (), payRefundEntity.getPayDeptId (), 1 );
            payRefundEntity.setPayDeptName ( payCompanyName );
            //处理最后一个审批人
            ActTaskCommentEntity actTaskCommentEntity = iActTaskCommentService.queryLastComment ( payRefundEntity.getInstanceId () );
            if ( actTaskCommentEntity != null ) {
                payRefundEntity.setApprovedUser ( actTaskCommentEntity.getApprovedUser () );
                payRefundEntity.setApprovedRemark ( actTaskCommentEntity.getRemark () );
            }
            //任务信息
            ActCurrentTaskVo currentTaskVo = iActModelService.getCurrentTaskByInstanceId ( payRefundEntity.getInstanceId () );
            Optional.ofNullable ( currentTaskVo ).ifPresent ( currentTask -> {
                payRefundEntity.setCurrentTask ( currentTask );
            } );
        } );
        Integer count = kingFinancePayRefundMapper.queryCount ( kingFinancePayRefundEntity );
        PageParam pageParam = new PageParam ();
        pageParam.setCurrent ( kingFinancePayRefundEntity.getPage () ).setSize ( kingFinancePayRefundEntities.size () ).setRecords ( kingFinancePayRefundEntities ).setTotal ( count );
        return AjaxResult.success ( pageParam );
    }

    /**
     * 查详情，送金蝶
     * @param id
     * @return
     */
    @Override
    public KingFinancePayRefundEntity querySingleById (String id) {
        KingFinancePayRefundEntity payRefundEntity = kingFinancePayRefundMapper.querySingleById ( id );
        String transactionCompanyCode = getCompanyName ( payRefundEntity.getTransactionCompanyType (), payRefundEntity.getTransactionCompanyId (), 2 );
        payRefundEntity.setTransactionCompanyCode ( transactionCompanyCode );
        String payDeptCode = getCompanyName ( payRefundEntity.getPayDeptType (), payRefundEntity.getPayDeptId (), 2 );
        payRefundEntity.setPayCompanyCode ( payDeptCode );
        return payRefundEntity;
    }

    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult updateSingle (KingFinancePayRefundDto kingFinancePayRefundDto) {
        KingFinancePayRefundEntity payRefundEntity = kingFinancePayRefundDto.getKingFinancePayRefundEntity ();
        payRefundEntity.setUpdateDate ( DateUtils.getNowDate () );
        payRefundEntity.setUpdateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        boolean b = this.updateById ( payRefundEntity );
        if ( ! b ) {
            throw new CustomException ( "修改付款退款单基本信息失败！" );
        }
        if ( payRefundEntity.getSourceDocType () == KingConstUtil.PAY_REFUND_TYPE_PAY ) {
            checkPayToRefund ( kingFinancePayRefundDto.getKingFinancePayRefundDetailEntities () );
        }
        updatePayRefundDetail ( kingFinancePayRefundDto.getKingFinancePayRefundDetailEntities (), payRefundEntity.getId () );
        return AjaxResult.success ();
    }

    /**
     * 删除
     * @param ids
     * @return
     */
    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult deleteByIds (String[] ids) {
        boolean update = this.update ( new LambdaUpdateWrapper<KingFinancePayRefundEntity> ().in ( KingFinancePayRefundEntity :: getId, ids ).set ( KingFinancePayRefundEntity :: getIsUsed, ConstUtil.IS_USED_NO ) );
        if ( ! update ) {
            throw new CustomException ( "删除付款退款单失败！" );
        }
        iKingFinancePayRefundDetailService.update ( new LambdaUpdateWrapper<KingFinancePayRefundDetailEntity> ().
                in ( KingFinancePayRefundDetailEntity :: getPayRefundId, ids ).
                set ( KingFinancePayRefundDetailEntity :: getIsUsed, ConstUtil.IS_USED_NO ) );
        return AjaxResult.success ();
    }

    /**
     * 付款退款单审核后业务处理
     * 1、如果有采购订单，更新退款情况
     * 2、修改状态
     * 3、推送金蝶
     * @param id
     * @return
     */
    @Override
    public AjaxResult approve (String id) {
        List<KingFinancePayRefundDetailEntity> list = iKingFinancePayRefundDetailService.list ( new LambdaQueryWrapper<KingFinancePayRefundDetailEntity> ().
                eq ( KingFinancePayRefundDetailEntity :: getPayRefundId, id ) );
        list.forEach ( detail -> {
            if ( StringUtils.isNotBlank ( detail.getOrderId () ) ) {
                OrderEntity orderEntity = iOrderService.getById ( detail.getOrderId () );
                String totalReturnAmount = orderEntity.getTotalReturnAmount (); // 采购订单退款金额
                String actualRefundMoney = detail.getActualRefundMoney (); // 实际退款金额
                boolean update = iOrderService.update ( new LambdaUpdateWrapper<OrderEntity> ().
                        set ( OrderEntity :: getTotalReturnAmount, CommonUtil.sumStr ( totalReturnAmount, actualRefundMoney, 2 ) ).
                        eq ( OrderEntity :: getId, orderEntity.getId () ) );
                if ( ! update ) {
                    throw new CustomException ( "修改采购订单退款金额失败！" );
                }
                log.info ( "付款退款单更新采购订单退款金额成功。" );
            }
        } );
        boolean update = this.update ( new LambdaUpdateWrapper<KingFinancePayRefundEntity> ().set ( KingFinancePayRefundEntity :: getState, ConstUtil.STATE_FINISH ).eq ( KingFinancePayRefundEntity :: getId, id ) );
        if ( ! update ) {
            throw new CustomException ( "修改付款退款单审核状态失败！" );
        }
        log.info ( "修改付款退款单审核状态成功。" );
        payRefundPushService.push ( id );
        return AjaxResult.success ("付款退款单审核完成并成功推送金蝶");
    }

    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult draft (KingFinancePayRefundDto kingFinancePayRefundDto) {
        KingFinancePayRefundEntity kingFinancePayRefundEntity = kingFinancePayRefundDto.getKingFinancePayRefundEntity ();
        kingFinancePayRefundEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
        String py = ChineseToPinyin.getFirstLetter ( "付款退款单" );
        int count = this.count ();
        String format = String.format ( "%08d", count );
        kingFinancePayRefundEntity.setDocNo ( py.concat ( format ) );
        kingFinancePayRefundEntity.setCreateDate ( DateUtils.getNowDate () );
        kingFinancePayRefundEntity.setCreateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        if ( kingFinancePayRefundEntity.getSourceDocType () == KingConstUtil.PAY_REFUND_TYPE_PAY ) {
            checkPayToRefund ( kingFinancePayRefundDto.getKingFinancePayRefundDetailEntities () );
        }
        //处理明细
        updatePayRefundDetail ( kingFinancePayRefundDto.getKingFinancePayRefundDetailEntities (), kingFinancePayRefundEntity.getId () );
        kingFinancePayRefundEntity.setState ( ConstUtil.STATE_IN_DRAFT );
        boolean save = this.save ( kingFinancePayRefundEntity );
        if ( ! save ) {
            throw new CustomException ( "暂存付款退款单失败！" );
        }
        return AjaxResult.success ( "暂存付款退款单失败", kingFinancePayRefundEntity.getId () );
    }

    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult submit (KingFinancePayRefundEntity kingFinancePayRefundEntity) {
        if ( StringUtils.isBlank ( kingFinancePayRefundEntity.getId () ) ) {
            return AjaxResult.error ( "请先暂存该单据！" );
        }
        //处理审批流
        StartProcessDto startDto = new StartProcessDto ().
                setBusinessId ( kingFinancePayRefundEntity.getId () ).
                setBusinessType ( kingFinancePayRefundEntity.getBusinessType () ).setModelId ( kingFinancePayRefundEntity.getModelId () ).
                setBusinessName ( "付款退款单失".concat ( kingFinancePayRefundEntity.getId () ) );
        String instanceId = iActModelService.startProcess ( startDto );
        boolean update = this.update ( new LambdaUpdateWrapper<KingFinancePayRefundEntity> ().
                set ( KingFinancePayRefundEntity :: getInstanceId, instanceId ).
                set ( KingFinancePayRefundEntity :: getState, ConstUtil.STATE_IN_PROCESS ).
                eq ( KingFinancePayRefundEntity :: getId, kingFinancePayRefundEntity.getId () ) );
        if ( ! update ) {
            throw new CustomException ( "提交付款退款单失败！" );
        }
        return AjaxResult.success ();
    }

    @Override
    public KingFinancePayRefundVo querySingle (String id) {
        KingFinancePayRefundVo vo = new KingFinancePayRefundVo ();
        KingFinancePayRefundEntity kingFinancePayRefundEntity = kingFinancePayRefundMapper.querySingleById ( id );
        String transactionCompanyName = getCompanyName ( kingFinancePayRefundEntity.getTransactionCompanyType (), kingFinancePayRefundEntity.getTransactionCompanyId (), 1 );
        kingFinancePayRefundEntity.setTransactionCompanyName ( transactionCompanyName );
        String payCompanyName = getCompanyName ( kingFinancePayRefundEntity.getPayDeptType (), kingFinancePayRefundEntity.getPayDeptId (), 1 );
        kingFinancePayRefundEntity.setPayDeptName ( payCompanyName );
        KingFinancePayRefundDetailEntity payRefundDetailEntity = new KingFinancePayRefundDetailEntity ();
        payRefundDetailEntity.setPayRefundId ( id );
        List<KingFinancePayRefundDetailEntity> list = iKingFinancePayRefundDetailService.selectPayRefundDetailList ( payRefundDetailEntity );
        vo.setKingFinancePayRefundEntity ( kingFinancePayRefundEntity ).setKingFinancePayRefundDetailEntities ( list );
        return vo;
    }

    /**
     * 新增付款退款单时校验规则
     * 退款单可退全部，可退其中一条明细，检查明细
     * 同一个付款单位（退款时供应商就是付款单位），实退金额不能大于该付款单未核销部分金额
     * @param kingFinancePayRefundDetailEntities
     */
    private void checkPayToRefund (List<KingFinancePayRefundDetailEntity> kingFinancePayRefundDetailEntities) {
        if ( ! CollectionUtils.isEmpty ( kingFinancePayRefundDetailEntities ) ) {
            //剔除自己后来新增的行
            kingFinancePayRefundDetailEntities = kingFinancePayRefundDetailEntities.stream ().filter ( detail -> StringUtils.isNotBlank ( detail.getSourceDetailId () ) ).collect ( Collectors.toList () );
            //源付款单明细行实退金额总和
            double sourceActualMoney = 0;
            for ( int i = 0; i < kingFinancePayRefundDetailEntities.size (); i++ ) {
                //1.退款单明细行
                KingFinancePayRefundDetailEntity payRefundDetailEntity = kingFinancePayRefundDetailEntities.get ( i );
                String actualMoney = payRefundDetailEntity.getActualRefundMoney (); // 明细行每行实退金额
                //2.源付款单明细
                KingFinancePayDetailEntity payDetailEntity = iKingFinancePayDetailService.getById ( payRefundDetailEntity.getSourceDetailId () );
                // 所有明细行的实退金额总和
                sourceActualMoney = CommonUtil.sum ( sourceActualMoney, Double.parseDouble ( payDetailEntity.getActualMoney () ) );
                //付款单未核销金额=总金额-已核销金额
                KingFinancePayEntity payEntity = iKingFinancePayService.getById ( payDetailEntity.getRelationId () );
                List<KingPayableReceivableVerificationDetailEntity> list = iKingPayableReceivableVerificationDetailService.list ( new LambdaQueryWrapper<KingPayableReceivableVerificationDetailEntity> ().eq ( KingPayableReceivableVerificationDetailEntity :: getDocNo, payEntity.getDocNo () ) );
                double sum = list.stream ().distinct ().mapToDouble ( verification -> Double.parseDouble ( verification.getVerificationAmount () ) ).sum ();
                if ( Double.parseDouble ( actualMoney ) > ( Double.parseDouble ( payDetailEntity.getActualMoney () ) - sum ) ) {
                    throw new CustomException ( "第" + payRefundDetailEntity.getLineNumber () + "行实退金额超限！" );
                }
            }
            //退款单明细行实退金额总和
            double sum = kingFinancePayRefundDetailEntities.stream ().mapToDouble ( amount -> Double.parseDouble ( amount.getActualRefundMoney () ) ).sum ();
            if ( sum > sourceActualMoney ) {
                throw new CustomException ( "实退金额总和不能超过源付款单可退金额！" );
            }
        }
    }

    private void updatePayRefundDetail (List<KingFinancePayRefundDetailEntity> kingFinancePayRefundDetailEntities, String payRefundId) {
        if ( ! CollectionUtils.isEmpty ( kingFinancePayRefundDetailEntities ) ) {
            iKingFinancePayRefundDetailService.remove ( new LambdaUpdateWrapper<KingFinancePayRefundDetailEntity> ().eq ( KingFinancePayRefundDetailEntity :: getPayRefundId, payRefundId ) );
            kingFinancePayRefundDetailEntities.forEach ( payRefundDetailEntity -> {
                payRefundDetailEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
                payRefundDetailEntity.setPayRefundId ( payRefundId );
                payRefundDetailEntity.setCreateDate ( DateUtils.getNowDate () );
                payRefundDetailEntity.setCreateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
                boolean save = iKingFinancePayRefundDetailService.save ( payRefundDetailEntity );
                if ( ! save ) {
                    throw new CustomException ( "更新付款退款单明细行失败！" );
                }
            } );
        }
    }


    /**
     * 根据往来单位类型，获取往来单位和付款单位
     * @param companyType：单位类型，根据往来单位或付款单位的类型魔法值
     * @param companyId：单位Id，根据单位类型，选择的具体单位Id
     * @param resultType：返回类型，可以返回单位名称或单位编码(送金蝶)
     * @return
     */
    private String getCompanyName (String companyType, String companyId, Integer resultType) {
        if ( StringUtils.isBlank ( companyType ) || StringUtils.isBlank ( companyId ) ) {
            return "";
        }
        String transactionCompany = "";
        switch ( companyType ) {
            case "BD_Customer":
                CustomerEntity customerEntity = iCustomerService.getById ( companyId );
                transactionCompany = ( resultType == 1 && customerEntity != null ) ? customerEntity.getCustomerName () : customerEntity.getCustomerCode ();
                break;
            case "BD_Supplier":
                SupplierEntity supplierEntity = iSupplierService.getById ( companyId );
                if ( supplierEntity != null )
                    transactionCompany = resultType == 1 ? supplierEntity.getSupplierName () : supplierEntity.getSupplierCode ();
                break;
            case "BD_Department":
                SysDept sysDept = iSysDeptService.getById ( companyId );
                if ( sysDept != null )
                    transactionCompany = resultType == 1 ? sysDept.getDeptName () : sysDept.getDeptCode ();
                break;
            case "BD_Empinfo":
                SysUser sysUser = iSysUserService.getById ( companyId );
                if ( sysUser != null )
                    transactionCompany = resultType == 1 ? sysUser.getUserName () : sysUser.getUserCode ();
                break;
            case "FIN_OTHERS":
                KingBaseTransactionsCompanyEntity transactionsCompanyEntity = iKingBaseTransactionsCompanyService.getById ( companyId );
                if ( transactionsCompanyEntity != null )
                    transactionCompany = resultType == 1 ? transactionsCompanyEntity.getTitle () : transactionsCompanyEntity.getCompanyCode ();
                break;
            case "ORG_Organizations":
                SysDept organ = iSysDeptService.getById ( companyId );
                if ( organ != null )
                    transactionCompany = resultType == 1 ? organ.getDeptName () : organ.getDeptCode ();
                break;
            case "BD_BANK":
                KingBaseBankEntity bankEntity = iKingBaseBankService.getById ( companyId );
                if ( bankEntity != null )
                    transactionCompany = resultType == 1 ? bankEntity.getTitle () : bankEntity.getCode ();
                break;
        }
        return transactionCompany;
    }

}
